﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Diagnostics;
using System.Windows.Forms;
using System.Threading;
using System.Drawing;

namespace WF_Xtension
{
	/// <summary>
	/// Creates FlickerProofForm oriented towards creating games with Windows Forms
	/// </summary>
	public class GameForm : FlickerProofForm
	{
		#region Variables
		protected List<FlickerProofPanel> m_drawnPanels;
		#endregion

		/// <sum	mary>
		/// Creates FlickerProofForm oriented towards creating games with Windows Forms
		/// that operates at 30 frames per second
		/// </summary>
		public GameForm() : this(30f) { }
		
		/// <summary>
		/// Creates FlickerProofForm oriented towards creating games with Windows Forms
		/// </summary>
		/// <param name="fps">Amount of update/draws per second</param>
		public GameForm(float fps) : base()
		{
			m_drawnPanels = new List<FlickerProofPanel>();
			FPS = fps;
		}

		#region PrivateMembers
		private void GameLoop()
		{
			int interval = (int)Math.Round(Stopwatch.Frequency / FPS, 0),//ticks till next update/draw
				panelDrawIndex = -1;//index of the panel in drawnPanels that will be drawn next. If it's less than 0, no drawing will be done
			long nextTick = Stopwatch.GetTimestamp() + interval, currentTick;
			double freqPerMS = (double)Stopwatch.Frequency / 1000;

			while (!this.IsDisposed && IsPlaying)
			{
				currentTick = Stopwatch.GetTimestamp();
				if (nextTick <= currentTick)
				{
					//next frame!
					GameUpdate(freqPerMS * (currentTick - nextTick));
					panelDrawIndex = 0;//start drawing

					nextTick = currentTick + interval;//reset time till next frame
				}

				#region Drawing
				if (panelDrawIndex >= 0)//draw on specific panels
				{
					if (m_drawnPanels.Count == 0)//draw using the entire window
					{
						Bitmap buffer = new Bitmap(Width, Height);

						using (Graphics g = Graphics.FromImage(buffer))
						{
							GameDraw(g);
						}

						BackgroundImage = buffer;
					}
					else
					{
						Bitmap buffer = new Bitmap(m_drawnPanels[panelDrawIndex].Width, m_drawnPanels[panelDrawIndex].Height);

						using (Graphics g = Graphics.FromImage(buffer))
						{
							GameDraw(panelDrawIndex, g);
						}

						m_drawnPanels[panelDrawIndex].Refresh();
						m_drawnPanels[panelDrawIndex].CreateGraphics().DrawImageUnscaled(buffer, 0, 0);
					}

					panelDrawIndex++;
					if (panelDrawIndex >= m_drawnPanels.Count)
						panelDrawIndex = -1;
					Thread.Sleep(1);//free up CPU

				}
				#endregion
			}
		}
		#endregion

		#region ProtectedMembers
		/// <summary>
		/// Starts the GameLoop
		/// </summary>
		protected void Initialize()
		{
			IsPlaying = true;
			GameLoop();
		}

		/// <summary>
		/// Updates the game
		/// </summary>
		/// <param name="updateTime">Time, in milliseconds, since last update</param>
		protected void GameUpdate(double updateTime)
		{
		}

		/// <summary>
		/// Draws the panel that is being updated
		/// </summary>
		/// <param name="panelDrawIndex">Index of the panel in drawnPanels to draw</param>
		/// <param name="draw">Graphics to use to draw the game screen</param>
		protected void GameDraw(int panelDrawIndex, Graphics draw)
		{
		}

		/// <summary>
		/// Draws the game screen
		/// </summary>
		/// <param name="draw">Graphics to use to draw the game screen</param>
		protected void GameDraw(Graphics draw)
		{
		}
		#endregion

		#region Accessors
		/// <summary>
		/// Frames per second of the GameForm.
		/// One update/draw call occurs each frame
		/// </summary>
		public float FPS { get; private set; }

		/// <summary>
		/// Whether or not the game loop is playing
		/// </summary>
		public bool IsPlaying { get; protected set; }
		#endregion
	}
}
